/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.openbookformat.obffinder;

import java.io.IOException;
import java.util.Observable;
import java.util.logging.Logger;
import javax.swing.ActionMap;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultEditorKit;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.netbeans.api.progress.ProgressRunnable;
import org.netbeans.api.progress.ProgressUtils;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.openide.util.ImageUtilities;
import org.netbeans.api.settings.ConvertAsProperties;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerUtils;
import org.openide.explorer.view.BeanTreeView;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Node;

/**
 * Top component which displays something.
 */
@ConvertAsProperties(dtd = "-//org.openbookformat.obffinder//OBFFindResult//EN",
autostore = false)
public final class OBFFindResultTopComponent extends TopComponent 
        implements ExplorerManager.Provider {

    private static OBFFindResultTopComponent instance;
    /** path to the icon used by the component and its open action */
    static final String ICON_PATH = "org/openbookformat/obffinder/application_view_detail.png";
    private static final String PREFERRED_ID = "OBFFindResultTopComponent";

    public OBFFindResultTopComponent() {
        initComponents();
        setName(NbBundle.getMessage(OBFFindResultTopComponent.class, "CTL_OBFFindResultTopComponent"));
        setToolTipText(NbBundle.getMessage(OBFFindResultTopComponent.class, "HINT_OBFFindResultTopComponent"));
        setIcon(ImageUtilities.loadImage(ICON_PATH, true));

        ActionMap map = this.getActionMap ();
        map.put(DefaultEditorKit.copyAction, ExplorerUtils.actionCopy(manager));
        map.put(DefaultEditorKit.cutAction,ExplorerUtils.actionCut(manager));
        map.put(DefaultEditorKit.pasteAction, ExplorerUtils.actionPaste(manager));
        associateLookup (ExplorerUtils.createLookup (manager, map));

        findChildren = new FindChildren();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        listPanel = new javax.swing.JPanel();
        jReplace = new javax.swing.JButton();
        jButton1 = new javax.swing.JButton();

        listPanel.setLayout(new java.awt.BorderLayout());

        org.openide.awt.Mnemonics.setLocalizedText(jReplace, org.openide.util.NbBundle.getMessage(OBFFindResultTopComponent.class, "OBFFindResultTopComponent.jReplace.text")); // NOI18N
        jReplace.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jReplaceActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(OBFFindResultTopComponent.class, "OBFFindResultTopComponent.jButton1.text")); // NOI18N
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jButton1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jReplace))
                    .addComponent(listPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 618, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(listPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jReplace)
                    .addComponent(jButton1))
                .addContainerGap())
        );

        view = new BeanTreeView();
        listPanel.add(view);
    }// </editor-fold>//GEN-END:initComponents

    private void jReplaceActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jReplaceActionPerformed
        final Node[] nodes = manager.getSelectedNodes();
        if(nodes.length  == 0)
            return;
             
        final ProgressHandle handle = ProgressHandleFactory.createHandle("Applying changes ...");
        //handle.start(nodes.length);
        //handle.switchToDeterminate(nodes.length);
        ProgressUtils.showProgressDialogAndRunLater(new ProgressRunnable<Void>() {
            @Override
            public Void run(ProgressHandle ph) {
//                StringBuilder buff = new StringBuilder();
//                StyledDocument lastDoc = null;
//                if(nodes.length > 0) {
//                    lastDoc = nodes[nodes.length -1].getLookup().lookup(
//                            FindResult.class).getEditorCookie().getDocument();
//                    try {
//                        buff.append(lastDoc.getText(0, lastDoc.getLength()));
//                    } catch (BadLocationException ex) {
//                        Exceptions.printStackTrace(ex);
//                    }
//                }
                
                ph.switchToDeterminate(100);
                for (int i = nodes.length - 1; i >= 0; i--) {
                    try {
                        FindResult r = null;
                        r = nodes[i].getLookup().lookup(FindResult.class);
                        if (r == null) {
                            continue;
                        }

//                            if(!r.getEditorCookie().getDocument().equals(lastDoc)) {
//                                lastDoc.remove(0, lastDoc.getLength());
//                                lastDoc.insertString(0, buff.toString(), null);
//                                lastDoc = r.getEditorCookie().getDocument();
//                                try {
//                                    buff = new StringBuilder();
//                                    buff.append(lastDoc.getText(0, lastDoc.getLength()));
//                                } catch (BadLocationException ex) {
//                                    Exceptions.printStackTrace(ex);
//                                }
//                            }
//                            
//                            buff.replace(r.getIndex(), r.getIndex() + r.getFind().length(), r.getReplace());
                        r.getEditorCookie().openDocument();
                        r.getEditorCookie().getDocument().remove(r.getIndex(), r.getFind().length());
                        r.getEditorCookie().getDocument().insertString(r.getIndex(), r.getReplace(), null);
                        int prog = (int) ((nodes.length - i) * 100.0 / nodes.length);
                        ph.progress("Processing ... [" + (nodes.length - i) + "/" + nodes.length + "]", prog);
                    } catch (IOException ex) {
                        Exceptions.printStackTrace(ex);
                    } catch (BadLocationException ex) {
                        Exceptions.printStackTrace(ex);
                    }

//                        lastDoc.remove(0, lastDoc.getLength());
//                        lastDoc.insertString(0, buff.toString(), null);


                            
                }
                ph.finish();
                return null;
            }
        }, handle, true);
    }//GEN-LAST:event_jReplaceActionPerformed

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
       view.expandAll();
    }//GEN-LAST:event_jButton1ActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton jButton1;
    private javax.swing.JButton jReplace;
    private javax.swing.JPanel listPanel;
    // End of variables declaration//GEN-END:variables
    /**
     * Gets default instance. Do not use directly: reserved for *.settings files only,
     * i.e. deserialization routines; otherwise you could get a non-deserialized instance.
     * To obtain the singleton instance, use {@link #findInstance}.
     */
    public static synchronized OBFFindResultTopComponent getDefault() {
        if (instance == null) {
            instance = new OBFFindResultTopComponent();
        }
        return instance;
    }

    /**
     * Obtain the OBFFindResultTopComponent instance. Never call {@link #getDefault} directly!
     */
    public static synchronized OBFFindResultTopComponent findInstance() {
        TopComponent win = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
        if (win == null) {
            Logger.getLogger(OBFFindResultTopComponent.class.getName()).warning(
                    "Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system.");
            return getDefault();
        }
        if (win instanceof OBFFindResultTopComponent) {
            return (OBFFindResultTopComponent) win;
        }
        Logger.getLogger(OBFFindResultTopComponent.class.getName()).warning(
                "There seem to be multiple components with the '" + PREFERRED_ID
                + "' ID. That is a potential source of errors and unexpected behavior.");
        return getDefault();
    }

    @Override
    public int getPersistenceType() {
        return TopComponent.PERSISTENCE_ALWAYS;
    }

    AbstractNode root = null;
    @Override
    public void componentOpened() {
        // TODO add custom code on component opening
        //listen for changes in the find component
        OBFFindnReplaceTopComponent.getDefault().getLookup().lookup(Observable.class).
                    addObserver(findChildren);

        root = new AbstractNode(findChildren);
        root.setDisplayName("Search Result");
        manager.setRootContext(root);
    }

    @Override
    public void componentClosed() {
        // TODO add custom code on component closing
        //listen for changes in the find component
        OBFFindnReplaceTopComponent.getDefault().getLookup().lookup(
                Observable.class).deleteObserver(findChildren);
    }
    
    void writeProperties(java.util.Properties p) {
        // better to version settings since initial version as advocated at
        // http://wiki.apidesign.org/wiki/PropertyFiles
        p.setProperty("version", "1.0");
        // TODO store your settings
    }

    Object readProperties(java.util.Properties p) {
        if (instance == null) {
            instance = this;
        }
        instance.readPropertiesImpl(p);
        return instance;
    }

    private void readPropertiesImpl(java.util.Properties p) {
        String version = p.getProperty("version");
        // TODO read your settings according to their version
    }

    @Override
    protected String preferredID() {
        return PREFERRED_ID;
    }

    @Override
    public ExplorerManager getExplorerManager() {
        return manager;
    }

    @Override
    protected void componentActivated() {
        ExplorerUtils.activateActions(manager, true);
    }

    @Override
    protected void componentDeactivated() {
        ExplorerUtils.activateActions(manager, false);
    }

    private FindChildren findChildren;
    private BeanTreeView view;
    private final ExplorerManager manager = new ExplorerManager();
}
